using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Xml;

namespace SuDokuSolution
{
	
	/// The Main grid holding all cells
	
	public class GridControl_9_by_9 :Grid
	{


        Constants1 one1 = new Constants1();
        private Graphics bufferGraphics;
        private Size cellSize;
        private Size subCellSize;
        private Point offset;
        private bool redrawBool = true;
        internal Cell_9_by_9[,] cellGrid;
        private int currentlyFilledCells = 0;
	

		
		/// Constructor for the grid control
		
		public GridControl_9_by_9()
		{  
			
		}

		
        
		

		
		/// Initiallize all the data of the class
	
		public void InitiallizeGrid()
		{
            cellGrid = new Cell_9_by_9[one1.nbOfCells, one1.nbOfCells];
			Brush selBrush=DrawingTools.WhiteBrush;
			int HB,VB;
			for(int row=0;row<one1.nbOfCells;row++)
			{
				HB=row/one1.nbOfLines;
				for(int col=0;col<one1.nbOfCells;col++)
				{					
					VB=col/one1.nbOfLines;
					if((HB+VB)%2!=0)
						selBrush=DrawingTools.BackGroundBrush1;
					else
						selBrush=DrawingTools.BackGroundBrush2;
                    cellGrid[row, col] = new Cell_9_by_9(this, selBrush);
				}
			}

			computeLocations();
		}


        public void OnEndGame(EventArgs e)
        {
            if (EndGame != null)
                EndGame(this, e);
        }
	
		


		
		/// Method called when control is resized
		
		protected override void OnResize(EventArgs e)
		{
			if(bufferBitmap==null || bufferBitmap.Size!=Size)
			{
				if(Size.Width!=0 && Size.Height!=0)//if not minimized
				{
                    bufferBitmap=new Bitmap(Size.Width,Size.Height);
					bufferGraphics=Graphics.FromImage(bufferBitmap);
					computeLocations();
				}
			}

            base.OnResize(e);
		}


	
		
		
		
		/// OnPaintBackground - overrides PaintBackground to avoid flickering
		
		protected override void OnPaintBackground(PaintEventArgs e)
		{}

	
		/// force cell repaint 
		
        public void RedrawCell(Cell_9_by_9 rcell)
		{
			rcell.Paint(bufferGraphics);
			Invalidate();
		}

		protected override void OnMouseDown(MouseEventArgs e)
		{
            if(!Enabled)
				return;
			
			if(e.X<offset.X || e.Y<offset.Y || 
				e.X>(offset.X+cellSize.Width*one1.nbOfCells)||
				e.Y>(offset.Y+cellSize.Height*one1.nbOfCells))
				return;
			
			int row,col;
			col=(e.X-offset.X)/cellSize.Width;
			row=(e.Y-offset.Y)/cellSize.Height;
			if(col>=one1.nbOfCells || row >=one1.nbOfCells)
				return;                    

			#region Code for processing clicks while playing game
			if(!generatingPuzzle)
			{
				if(!isGameStarted)
					ExecuteStartGame();
            
				bool changed=false;
				if(e.Clicks==1)
				{
					cellGrid[row,col].MouseDown(e);
				}
				else if(e.Clicks==2)
				{
					cellGrid[row,col].DoubleClick(e);
					changed=true;
				}

				if(changed)
				{
					//if an invalid nb has been selected mark it as error
					if(cellGrid[row,col].cellData.problemNb!=one1.NoNumber 
						&& !cellGrid[row,col].cellData.candidateNb.Contains(cellGrid[row,col].cellData.problemNb))
					{
						cellGrid[row,col].MarkError=true;      //if an invalid nb has been clicked
					}
					else
					{
						cellGrid[row,col].MarkError=false;
						autoUpdateMarkedCandidates(row,col);					
					}				
					this.redrawBool=true;
					currentlyFilledCells+=(cellGrid[row,col].cellData.problemNb==one1.NoNumber )?(-1):1;
					if(currentlyFilledCells==one1.nbOfCells*one1.nbOfCells)
						CheckEndGame();
				}
				base.OnMouseDown(e);
				Invalidate();
			}
				#endregion

			#region code for processing clicks while generating a problem manually
			else
			{
                Cell_9_by_9 gr = cellGrid[row, col];
				int clickedNb=gr.getClickedNb(e.X-gr.cellLocation.X,e.Y-gr.cellLocation.Y);
			
				if(gr.cellData.problemNb==one1.NoNumber)
				{
					gr.cellData.problemNb=clickedNb;
					gr.cellData.readOnly=true;
				}
				else
					gr.cellData.problemNb=one1.NoNumber;
                RedrawCell(gr);
			}
			#endregion
		}


        /// To compute location,size etc of grid or cells
        public void computeLocations()
        {
            if (cellGrid == null)
                return;

            cellSize = new Size(((Size.Width / one1.nbOfCells) / one1.nbOfLines) * one1.nbOfLines - 10,
                ((Size.Height / one1.nbOfCells) / one1.nbOfLines) * one1.nbOfLines - 8);

            subCellSize = new Size(cellSize.Width / one1.nbOfLines, cellSize.Height / one1.nbOfLines);

            //Fonts
            DrawingTools.SelectedFont = new Font("Baskerville Old Face", cellSize.Height / 2, FontStyle.Bold);
            DrawingTools.miniSelectedFont = new Font("Baskerville Old Face", subCellSize.Height / 2, FontStyle.Regular);

            offset = new Point();
            offset.X = (Size.Width - cellSize.Width * one1.nbOfCells) / 2;
            offset.Y = (Size.Height - cellSize.Height * one1.nbOfCells) / 2;

            for (int row = 0; row < one1.nbOfCells; row++)
            {
                for (int col = 0; col < one1.nbOfCells; col++)
                {
                    cellGrid[row, col].cellSize = cellSize;
                    cellGrid[row, col].subCellSize = subCellSize;
                    cellGrid[row, col].cellLocation = new Point(offset.X + col * cellSize.Width,
                        offset.Y + row * cellSize.Height);
                    cellGrid[row, col].cellRect = new Rectangle(cellGrid[row, col].cellLocation.X + 1,
                        cellGrid[row, col].cellLocation.Y + 1, cellSize.Width - 2, cellSize.Height - 2);
                }
            }
            redrawBool = true;
            Invalidate();
        }
		///autometically update all candidate nbs when a valid nb is selected in a cell
		///  The Validity check should be performed by the calling function
		
		private void autoUpdateMarkedCandidates(int row,int col)
		{
			if(autoUpdate)
			{			
				for(int i=0;i<9;i++)
				{
					searchCandidates(row,i);
					cellGrid[row,i].CheckedNbs.Clear();				
					foreach(object ob in cellGrid[row,i].cellData.candidateNb)
						cellGrid[row,i].CheckedNbs.Add(ob);
					if(row!=i)
					{
						searchCandidates(i,col);
						cellGrid[i,col].CheckedNbs.Clear();				
						foreach(object ob in cellGrid[i,col].cellData.candidateNb)
							cellGrid[i,col].CheckedNbs.Add(ob);				
					}
				}
			
				int HB=(int)(row/one1.nbOfLines);
				int VB=(int) (col/one1.nbOfLines);            
                          
				for(int i=HB*one1.nbOfLines;i<(HB+1)*one1.nbOfLines;i++)
				{
					for(int j=VB*one1.nbOfLines;j<(VB+1)*one1.nbOfLines;j++)
					{
						if(i!=row && j!=col )
						{
							
							searchCandidates(i,j);
							cellGrid[i,j].CheckedNbs.Clear();				
							foreach(object ob in cellGrid[i,j].cellData.candidateNb)
								cellGrid[i,j].CheckedNbs.Add(ob);				
						}
					}
				}
			}

			else
			{
				for(int i=0;i<9;i++)
				{
					searchCandidates(row,i);
					if(row!=i)
					{
						searchCandidates(i,col);
					}
				}
			
				int HB=(int)(row/one1.nbOfLines);
				int VB=(int) (col/one1.nbOfLines);            
                          
				for(int i=HB*one1.nbOfLines;i<(HB+1)*one1.nbOfLines;i++)
				{
					for(int j=VB*one1.nbOfLines;j<(VB+1)*one1.nbOfLines;j++)
					{
						if(i!=row && j!=col )
						{
							searchCandidates(i,j);
						}
					}
				}
			}

		}

	
		/// searches candidate nbs for a cell given by row,col 
		
		public void searchCandidates(int row,int col)
		{
			if(cellGrid[row,col].cellData.problemNb!=one1.NoNumber)
			{
                cellGrid[row,col].cellData.candidateNb.Clear();
				return;
			}
			
			int HB=(int)(row/one1.nbOfLines);
			int VB=(int) (col/one1.nbOfLines);
            
			//clear the existing list
			cellGrid[row,col].cellData.candidateNb.Clear();
            
			
			foreach (int number in new int[9]{1,2,3,4,5,6,7,8,9})
			{
				cellGrid[row,col].cellData.candidateNb.Add(number);
			}                   
            
			for(int i=0;i<9;i++)
			{
				cellGrid[row,col].cellData.candidateNb.Remove(cellGrid[row,i].cellData.problemNb);
				cellGrid[row,col].cellData.candidateNb.Remove(cellGrid[i,col].cellData.problemNb);
			}
                          
			for(int i=HB*one1.nbOfLines;i<(HB+1)*one1.nbOfLines;i++)
			{
				for(int j=VB*one1.nbOfLines;j<(VB+1)*one1.nbOfLines;j++)
				{
					cellGrid[row,col].cellData.candidateNb.Remove(cellGrid[i,j].cellData.problemNb);                                    
				}
			}			
		}



        /// called when Control has to be painted

        protected override void OnPaint(PaintEventArgs e)
        {
            if (redrawBool)
            {
                Pen selPen = DrawingTools.MediumPen;
                bufferGraphics.FillRectangle(DrawingTools.WhiteBrush, e.ClipRectangle);
                for (int i = 0; i <= one1.nbOfCells; i++)
                {
                    if (i % one1.nbOfLines == 0)
                        selPen = DrawingTools.DarkPen;
                    else
                        selPen = DrawingTools.MediumPen;
                    //horizontal line
                    bufferGraphics.DrawLine(selPen, offset.X, offset.Y + cellSize.Height * i,
                        offset.X + cellSize.Width * one1.nbOfCells, offset.Y + cellSize.Height * i);

                    //vertical line
                    bufferGraphics.DrawLine(selPen, offset.X + cellSize.Width * i, offset.Y,
                        offset.X + cellSize.Width * i, offset.Y + cellSize.Height * one1.nbOfCells);
                }

                for (int row = 0; row < one1.nbOfCells; row++)
                {
                    for (int col = 0; col < one1.nbOfCells; col++)
                    {
                        cellGrid[row, col].Paint(bufferGraphics);
                    }
                }
            }

            e.Graphics.DrawImage(bufferBitmap, 0, 0);
            redrawBool = false;
        }
		
		/// Check if Game has ended
	
		private void CheckEndGame()
		{
			int row,col;
			if(varify(out row,out col))
				OnEndGame(EventArgs.Empty);			
		}

		
		/// Returns true if the current grid follows all rules of su doku else the
		/// Index of wrong entry in the wRow,wCol
		
		internal bool varify(out int wRow,out int wCol)
		{
			for(int row=0;row<one1.nbOfCells;row++)
			{
				for(int col=0;col<one1.nbOfCells;col++)
				{
					int HB=(int)(row/one1.nbOfLines);
					int VB=(int) (col/one1.nbOfLines);
					int nb=cellGrid[row,col].cellData.problemNb;
					if(nb==one1.NoNumber)
						continue;
					for(int i=0;i<one1.nbOfCells;i++)
					{
						if((cellGrid[row,i].cellData.problemNb==nb && i!=col) || (cellGrid[i,col].cellData.problemNb ==nb && i!=row))
						{
							wRow=row+1;
							wCol=col+1;
							return false;
						}
					}
                          
					for(int i=HB*3;i<(HB+1)*3;i++)
					{
						for(int j=VB*3;j<(VB+1)*3;j++)
						{
							if(cellGrid[i,j].cellData.problemNb==nb && i!=row && i!=col)
							{
								wRow=row+1;
								wCol=col+1;
								return false;
							}                                    
						}
					}      
				}
			}

			wRow=wCol=one1.NoNumber;
			return true;
		}

		
		/// Load Data in the Grid 
		
		public void LoadData(int [,]completeGrid,int [,]problemGrid)
		{
			this.currentlyFilledCells=0;
			for(int row=0;row<one1.nbOfCells;row++)
			{
				for(int col=0;col<one1.nbOfCells;col++)
				{
					this.cellGrid[row,col].cellData.originalNb=completeGrid[row,col];
					this.cellGrid[row,col].cellData.problemNb=problemGrid[row,col];
					if(problemGrid[row,col]==one1.NoNumber)
						this.cellGrid[row,col].cellData.readOnly=false;
					else 
					{
						this.cellGrid[row,col].cellData.readOnly=true;
						this.currentlyFilledCells++;
					}
					this.cellGrid[row,col].CheckedNbs.Clear();
				}
			}
			
			for(int row=0;row<one1.nbOfCells;row++)
			 {
				 for(int col=0;col<one1.nbOfCells;col++)
				 {
					 searchCandidates(row,col);
				 }
			 }

            this.redrawBool=true;
			Invalidate();
		}

		
		/// Save game to file
		
		public  bool SaveGameToFile( string fileName)
		{
			XmlTextWriter xtw = new XmlTextWriter(fileName,System.Text.UTF8Encoding.UTF8);

			xtw.WriteStartDocument();
			
			// save map data
			xtw.WriteStartElement("mapData");
			
			string checkedNbStr;
			//int cellsNumber = mapData.CellDataArray.GetLength(0);
			for (int row=0 ; row<one1.nbOfCells ; row++)
			{
				for (int col=0 ; col<one1.nbOfCells ; col++)
				{
					xtw.WriteStartElement("Data");

					// original nb attribute
					xtw.WriteStartAttribute("originalNumber", string.Empty);
					xtw.WriteRaw(cellGrid[row,col].cellData.originalNb.ToString());
					xtw.WriteEndAttribute();

					
					// read only attribute
					xtw.WriteStartAttribute("readonly", string.Empty);
					xtw.WriteRaw(cellGrid[row,col].cellData.readOnly.ToString());
					xtw.WriteEndAttribute();

					// selected number attribute
					xtw.WriteStartAttribute("problemNumber", string.Empty);
					xtw.WriteRaw(cellGrid[row,col].cellData.problemNb.ToString());
					xtw.WriteEndAttribute();

					// marked values attribute
					xtw.WriteStartAttribute("checkedNumbers", string.Empty);
					
					checkedNbStr= "";
					for (int k=0 ; k< cellGrid[row,col].CheckedNbs.Count; k++)
					{
						checkedNbStr+=((int)cellGrid[row,col].CheckedNbs[k]).ToString();
					}

					xtw.WriteRaw(checkedNbStr);
					
					//xtw.WriteEndAttribute();

					xtw.WriteEndElement();
				}
			}

			xtw.WriteEndElement();
			xtw.Close();
            return true;
		}

	
		/// Load game data from file
		
		public bool LoadGameFromFile(string fileName)
		{
			// check that file exists
			if(!File.Exists(fileName))
			{
				MessageBox.Show("The specified does not exist","Error"); 
				return false;
			}
			
			try
			{
				this.currentlyFilledCells=0;
				System.Xml.XmlDocument xdoc = new XmlDocument();
				xdoc.Load(fileName);
				XmlElement root = xdoc.DocumentElement;
				System.Xml.XmlNodeList nodeList;
				
			
				nodeList = root.ChildNodes;//.Item(0).ChildNodes;
				
				bool readOnly=false;;
				int onb=0,pnb=0;
				int k=0;
      			string checkedNbStr;
				
				for (int row=0 ; row<one1.nbOfCells ; row++)
				{
					for (int col=0 ; col<one1.nbOfCells ; col++)
					{
						readOnly=Convert.ToBoolean(nodeList.Item(k).Attributes["readonly"].Value);
						pnb= Convert.ToInt32(nodeList.Item(k).Attributes["problemNumber"].Value);
						onb=Convert.ToInt32(nodeList.Item(k).Attributes["originalNumber"].Value);

                        			
						
						cellGrid[row,col].cellData.readOnly =readOnly;
						cellGrid[row,col].cellData.problemNb=pnb;
						cellGrid[row,col].cellData.originalNb= onb;

						if(pnb!=one1.NoNumber)
						{
							this.currentlyFilledCells++;
						}

						checkedNbStr = Convert.ToString(nodeList.Item(k).Attributes["checkedNumbers"].Value);
						
						cellGrid[row,col].CheckedNbs.Clear();
						for (int m=0 ; m<checkedNbStr.Length ;m++)
						{
							cellGrid[row,col].CheckedNbs.Add(Convert.ToInt32(checkedNbStr[m].ToString()));
						}
						k++;
					}
				}
				for(int row=0;row<one1.nbOfCells;row++)
				{
					for(int col=0;col<one1.nbOfCells;col++)
					{
						searchCandidates(row,col);
					}
				}
				redrawBool=true;
				Invalidate();
				this.Enabled=true;
			}
			catch(Exception ex)
			{
				MessageBox.Show("The File "+fileName+" is corrupt\n"+ex.Message,"Error");
				return false;
			}
            return true;
		}

		public void showNext()
		{
			for(int row=0;row<one1.nbOfCells;row++)
			{
				for(int col=0;col<one1.nbOfCells;col++)
				{
				   if(cellGrid[row,col].cellData.problemNb!=one1.NoNumber)
				    	continue;
					int HB=(int)(row/one1.nbOfLines);
					int VB=(int) (col/one1.nbOfLines);
					if(cellGrid[row,col].cellData.candidateNb.Count==1)
					{						
						cellGrid[row,col].cellData.problemNb=(int)cellGrid[row,col].cellData.candidateNb[0];
						currentlyFilledCells++;
						autoUpdateMarkedCandidates(row,col);
						redrawBool=true;
						Invalidate();
						return;						
					}
						
					else   //find nb that cannot come into any cell of that block
					{
						for(int index=0;index<cellGrid[row,col].cellData.candidateNb.Count;index++)
						{
							int number=(int)cellGrid[row,col].cellData.candidateNb[index];
							bool found=false;
							for(int i=HB*one1.nbOfLines;i<(HB+1)*one1.nbOfLines;i++)                                
								for(int j=VB*one1.nbOfLines;j<(VB+1)*one1.nbOfLines;j++)                                    
									if(cellGrid[i,j].cellData.candidateNb.Contains(number) && !(i==row && j==col))
									{
										j=(VB+1)*one1.nbOfLines; //exit from loop
										i=(HB+1)*one1.nbOfLines;
										found =true;
									}                                   
							if(found)
								continue;
							else
							{
								cellGrid[row,col].cellData.problemNb=number;								
								//cellGrid[row,col].cellData.candidateNb.Clear();
								autoUpdateMarkedCandidates(row,col);
								currentlyFilledCells++;
								redrawBool=true;
								Invalidate();
								return;
							}
						}											
					}
					
				}
			}
		}
	}
}
